package com.hjw.dynamicdatasource.util;

import com.hjw.dynamicdatasource.util.DatabaseMetadataUtil.ColumnInfo;
import com.hjw.dynamicdatasource.util.DatabaseMetadataUtil.TableInfo;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 动态类生成器，用于在内存中动态生成实体类
 * @author ivan
 */
@Slf4j
@Component
public class DynamicClassGenerator {

    private volatile URLClassLoader currentClassLoader;
    private LoaderClassPath loaderClassPath;
    private ClassPool classPool;
    private String currentDataSourceKey = "default";
    
    public DynamicClassGenerator(){
        resetClassLoadingEnvironment();
    }
    private void resetClassLoadingEnvironment(){
        // 创建一个类加载器，用于加载动态生成的类
        this.currentClassLoader = new URLClassLoader(new URL[0], null) {
            @Override
            public String toString() {
                return "IsolatedClassLoader@" + Integer.toHexString(hashCode()) + "(dataSource:" + currentDataSourceKey + ")";
            }

            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                log.debug("类加载器被GC: {}", this);
            }
        };

        // 创建新的ClassPool，避免污染
        this.classPool = new ClassPool(null);
        this.classPool.appendSystemPath();
    }
    
    /**
     * 设置当前数据源标识
     * @param dataSourceKey 数据源标识
     */
    public void setCurrentDataSourceKey(String dataSourceKey) {
        // 如果数据源改变，清空缓存并重置类加载环境
        if (!this.currentDataSourceKey.equals(dataSourceKey)) {
            this.currentDataSourceKey = dataSourceKey;
            clearCache();
            log.info("已切换数据源并重置类加载环境: {}", dataSourceKey);
        }
    }
    
    /**
     * 获取当前数据源标识
     * @return 数据源标识
     */
    public String getCurrentDataSourceKey() {
        return currentDataSourceKey;
    }
    
    /**
     * 缓存已生成的类，避免重复生成。键格式："数据源标识:类名"
     */
    private Map<String, Class<?>> classCache = new ConcurrentHashMap<>();
    
    /**
     * 根据表信息动态生成实体类
     * @param tableInfo 表信息
     * @return 生成的类
     * @throws Exception 生成过程中的异常
     */
    public Class<?> generateEntityClass(TableInfo tableInfo) throws Exception {
        String className = generateClassName(tableInfo.getTableName());
        String cacheKey = currentDataSourceKey + ":" + className;
        
        // 检查缓存
        if (classCache.containsKey(cacheKey)) {
            return classCache.get(cacheKey);
        }
        
        // 为不同数据源的同名表生成不同的类名，避免类加载器冲突
        String qualifiedClassName = "com.hjw.dynamicdatasource.entity.dynamic." + 
            (currentDataSourceKey.equals("default") ? "" : currentDataSourceKey + ".") + 
            className;
        
        CtClass ctClass = null;
        try {
            // 确保ClassPool被正确初始化
            if (classPool == null) {
                resetClassLoadingEnvironment();
            }
            
            // 使用当前类加载器的classpath
            this.loaderClassPath = new LoaderClassPath(currentClassLoader);
            classPool.appendClassPath(loaderClassPath);
            
            // 创建类
            ctClass = classPool.makeClass(qualifiedClassName);
            
            // 添加lombok注解
            addLombokAnnotation(ctClass);
            
            // 添加表名注解
            addTableNameAnnotation(ctClass, tableInfo.getTableName());
            
            // 添加字段
            Map<String, CtField> fields = addFields(ctClass, tableInfo.getColumns());
            
            // 添加getter和setter方法
            addGetterSetterMethods(ctClass, fields);

            // 重写toString方法
            addToStringMethod(ctClass, fields);
            
            // 生成并加载类
            Class<?> clazz = ctClass.toClass(currentClassLoader, null);
            
            // 使用数据源标识+类名作为缓存键
            classCache.put(cacheKey, clazz);
            
            log.info("为数据源[{}]生成实体类成功: {}", currentDataSourceKey, qualifiedClassName);
            
            return clazz;
        } catch (CannotCompileException e) {
            // 特殊处理类加载失败的情况
            log.error("为数据源[{}]编译类失败: {}", currentDataSourceKey, qualifiedClassName, e);
            // 如果是类已存在异常，尝试清理缓存和类加载器
            if (e.getMessage() != null && e.getMessage().contains("duplicate class definition")) {
                log.warn("检测到类重复定义，尝试重置类加载环境...");
                clearCache();
                // 重新尝试生成
                return generateEntityClass(tableInfo);
            }
            throw e;
        } catch (Exception e) {
            log.error("为数据源[{}]生成实体类失败: {}", currentDataSourceKey, qualifiedClassName, e);
            throw e;
        } finally {
            // 释放CtClass，避免内存泄漏
            if (ctClass != null) {
                try {
                    ctClass.detach();
                } catch (Exception ignore) {
                }
            }
            // 清理classpath，避免累积
            if (loaderClassPath != null) {
                classPool.removeClassPath(loaderClassPath);
            }
        }
    }

    private void addToStringMethod(CtClass ctClass, Map<String, CtField> fields) throws Exception {
        // 1. 修正返回值类型：toString() 必须返回 String
        CtClass stringType = ClassPool.getDefault().get("java.lang.String");
        CtMethod toStringMethod = new CtMethod(stringType, "toString", null, ctClass);

        // 2. 处理字段拼接（生成 "field1=值1, field2=值2" 格式）
        String fieldsStr = fields.values().stream()
                .map(field -> {
                    try {
                        String fieldName = field.getName();
                        // 判断是否为 String 类型（需要特殊处理引号）
                        boolean isString = field.getType().getName().equals("java.lang.String");
                        // 字段值表达式：null 显示 "null"，String 类型用 Objects.toString 避免直接拼接
                        String valueExpr = isString
                                ? "java.util.Objects.toString(" + fieldName + ")"
                                : fieldName;
                        // 拼接单个字段的表达式："fieldName=" + (fieldName == null ? "null" : valueExpr)
                        return String.format("\"%s=\" + (%s == null ? \"null\" : %s)",
                                fieldName, fieldName, valueExpr);
                    } catch (NotFoundException e) {
                        throw new RuntimeException(e);
                    }
                })
                .collect(Collectors.joining(" + \", \" + "));

        // 3. 生成完整的方法体（格式："类名{field1=值1, field2=值2}"）
        String className = ctClass.getSimpleName();
        String methodBody = String.format(
                "return \"%s{\" + %s + \"}\";",
                className,
                fieldsStr.isEmpty() ? "" : fieldsStr
        );

        // 4. 设置方法体并添加到类中
        toStringMethod.setBody(methodBody);
        toStringMethod.setModifiers(Modifier.PUBLIC);
        ctClass.addMethod(toStringMethod);
    }

    /**
     * 生成类名
     * @param tableName 表名
     * @return 驼峰命名的类名
     */
    private String generateClassName(String tableName) {
        // 移除表名前缀（如t_、sys_等）
        if (tableName.contains("_")) {
            String[] parts = tableName.split("_");
            StringBuilder className = new StringBuilder();
            
            for (String part : parts) {
                if (part.length() > 0) {
                    className.append(Character.toUpperCase(part.charAt(0)));
                    if (part.length() > 1) {
                        className.append(part.substring(1).toLowerCase());
                    }
                }
            }
            
            return className.toString();
        } else {
            // 如果没有下划线，直接首字母大写
            return Character.toUpperCase(tableName.charAt(0)) + tableName.substring(1);
        }
    }
    
    /**
     * 添加Lombok的Data注解
     * @param ctClass CtClass对象
     */
    private void addLombokAnnotation(CtClass ctClass) throws Exception {
        ConstPool constPool = ctClass.getClassFile().getConstPool();
        AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation dataAnnotation = new Annotation("lombok.Data", constPool);
        attr.addAnnotation(dataAnnotation);
        ctClass.getClassFile().addAttribute(attr);
    }
    
    /**
     * 添加MyBatis-Plus的TableName注解
     * @param ctClass CtClass对象
     * @param tableName 表名
     */
    private void addTableNameAnnotation(CtClass ctClass, String tableName) throws Exception {
        ConstPool constPool = ctClass.getClassFile().getConstPool();
        AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation tableNameAnnotation = new Annotation("com.baomidou.mybatisplus.annotation.TableName", constPool);
        tableNameAnnotation.addMemberValue("value", new StringMemberValue(tableName, constPool));
        attr.addAnnotation(tableNameAnnotation);
        ctClass.getClassFile().addAttribute(attr);
    }
    
    /**
     * 添加字段
     * @param ctClass CtClass对象
     * @param columns 列信息列表
     * @return 字段映射
     */
    private Map<String, CtField> addFields(CtClass ctClass, List<ColumnInfo> columns) throws Exception {
        Map<String, CtField> fields = new HashMap<>();
        
        for (ColumnInfo column : columns) {
            String fieldName = generateFieldName(column.getColumnName());
            String fieldType = getFieldType(column.getJavaType());
            
            // 创建字段
            CtField ctField = new CtField(classPool.get(fieldType), fieldName, ctClass);
            ctField.setModifiers(Modifier.PRIVATE);
            
            // 添加字段注解
            addFieldAnnotations(ctField, column, fieldName);
            
            ctClass.addField(ctField);
            fields.put(fieldName, ctField);
        }
        
        return fields;
    }
    
    /**
     * 生成字段名（驼峰命名）
     * @param columnName 列名
     * @return 驼峰命名的字段名
     */
    private String generateFieldName(String columnName) {
        String[] parts = columnName.split("_");
        StringBuilder fieldName = new StringBuilder();
        
        // 第一个单词全小写
        if (parts.length > 0 && parts[0].length() > 0) {
            fieldName.append(parts[0].toLowerCase());
        }
        
        // 后续单词首字母大写
        for (int i = 1; i < parts.length; i++) {
            if (parts[i].length() > 0) {
                fieldName.append(Character.toUpperCase(parts[i].charAt(0)));
                if (parts[i].length() > 1) {
                    fieldName.append(parts[i].substring(1).toLowerCase());
                }
            }
        }
        
        return fieldName.toString();
    }
    
    /**
     * 获取字段类型的完整类名
     * @param javaType Java类型名
     * @return 完整的类名
     */
    private String getFieldType(String javaType) {
        switch (javaType) {
            case "Integer":
                return "java.lang.Integer";
            case "Long":
                return "java.lang.Long";
            case "String":
                return "java.lang.String";
            case "Date":
            case "java.util.Date":
                return "java.util.Date";
            case "BigDecimal":
                return "java.math.BigDecimal";
            case "Double":
                return "java.lang.Double";
            case "Boolean":
                return "java.lang.Boolean";
            default:
                return "java.lang.String";
        }
    }
    
    /**
     * 添加字段注解
     * @param ctField 字段
     * @param column 列信息
     * @param fieldName 字段名
     */
    private void addFieldAnnotations(CtField ctField, ColumnInfo column, String fieldName) throws Exception {
        ConstPool constPool = ctField.getFieldInfo().getConstPool();
        AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        
        // 添加TableField注解
        Annotation tableFieldAnnotation = new Annotation("com.baomidou.mybatisplus.annotation.TableField", constPool);
        tableFieldAnnotation.addMemberValue("value", new StringMemberValue(column.getColumnName(), constPool));
        attr.addAnnotation(tableFieldAnnotation);
        
        // 如果是主键，添加TableId注解
        if (column.isPrimaryKey()) {
            Annotation tableIdAnnotation = new Annotation("com.baomidou.mybatisplus.annotation.TableId", constPool);
            attr.addAnnotation(tableIdAnnotation);
        }
        
        ctField.getFieldInfo().addAttribute(attr);
    }
    
    /**
     * 添加getter和setter方法
     * @param ctClass CtClass对象
     * @param fields 字段映射
     */
    private void addGetterSetterMethods(CtClass ctClass, Map<String, CtField> fields) throws Exception {
        for (Map.Entry<String, CtField> entry : fields.entrySet()) {
            String fieldName = entry.getKey();
            CtField ctField = entry.getValue();
            String fieldType = ctField.getType().getName();
            
            // 添加getter方法
            String getterName = "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
            CtMethod getterMethod = CtNewMethod.getter(getterName, ctField);
            ctClass.addMethod(getterMethod);
            
            // 添加setter方法
            String setterName = "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
            CtMethod setterMethod = CtNewMethod.setter(setterName, ctField);
            ctClass.addMethod(setterMethod);
        }
    }
    
    /**
     * 创建实体类实例
     * @param clazz 实体类
     * @param data 数据映射
     * @return 实体类实例
     * @throws Exception 创建过程中的异常
     */
    public Object createEntityInstance(Class<?> clazz, Map<String, Object> data) throws Exception {
        Object instance = clazz.newInstance();
        
        for (Map.Entry<String, Object> entry : data.entrySet()) {
            String fieldName = generateFieldName(entry.getKey());
            Object value = entry.getValue();
            
            // 设置字段值
            setFieldValue(instance, fieldName, value);
        }
        
        return instance;
    }
    
    /**
     * 设置字段值
     * @param instance 实例对象
     * @param fieldName 字段名
     * @param value 字段值
     * @throws Exception 设置过程中的异常
     */
    private void setFieldValue(Object instance, String fieldName, Object value) throws Exception {
        String setterName = "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
        Class<?> clazz = instance.getClass();
        
        // 查找setter方法
        for (Method method : clazz.getMethods()) {
            if (method.getName().equals(setterName) && method.getParameterCount() == 1) {
                Class<?> paramType = method.getParameterTypes()[0];
                // 类型转换
                Object convertedValue = convertValue(value, paramType);
                method.invoke(instance, convertedValue);
                break;
            }
        }
    }
    
    /**
     * 转换值类型
     * @param value 值
     * @param targetType 目标类型
     * @return 转换后的值
     */
    public Object convertValue(Object value, Class<?> targetType) {
        if (value == null) {
            return null;
        }
        
        // 如果类型已经匹配，直接返回
        if (targetType.isInstance(value)) {
            return value;
        }
        
        // 进行常见类型转换
        String valueStr = value.toString();
        
        if (targetType == Integer.class || targetType == int.class) {
            return Integer.parseInt(valueStr);
        } else if (targetType == Long.class || targetType == long.class) {
            return Long.parseLong(valueStr);
        } else if (targetType == Double.class || targetType == double.class) {
            return Double.parseDouble(valueStr);
        } else if (targetType == Boolean.class || targetType == boolean.class) {
            return Boolean.parseBoolean(valueStr);
        } else if (targetType == Date.class) {
            // 简单的日期转换，实际应用中可能需要更复杂的处理
            if (value instanceof java.sql.Date) {
                return new Date(((java.sql.Date) value).getTime());
            } else if (value instanceof Timestamp) {
                return new Date(((Timestamp) value).getTime());
            }
            // 尝试解析字符串为日期
            try {
                return new Date(Long.parseLong(valueStr));
            } catch (NumberFormatException e) {
                return new Date();
            }
        }
        
        // 默认转换为字符串
        return valueStr;
    }
    
    /**
     * 清除类缓存
     */
    public void clearCache() {
        URLClassLoader oldLoader = currentClassLoader;
        WeakReference<ClassLoader> monitorRef = new WeakReference<>(oldLoader);

        // 清除缓存
        classCache.clear();
        
        // 关闭旧的类加载器
        if (currentClassLoader != null) {
            try {
                currentClassLoader.close();
                log.debug("已关闭旧类加载器: {}", oldLoader);
            } catch (IOException e) {
                log.warn("关闭类加载器时出错: {}", e.getMessage());
            }
            // 帮助GC回收
            currentClassLoader = null;
        }

        // 创建全新的类加载环境
        resetClassLoadingEnvironment();
        
        // 清理ClassPool以避免类冲突
        if (classPool != null) {
            // 清除所有已加载的类
            classPool.clearImportedPackages();
        }

        // 尝试强制GC回收旧的类加载器
        try {
            // 调用 GC
            Runtime.getRuntime().gc();
            Thread.sleep(50);
            // 调用 finalization
            Runtime.getRuntime().runFinalization();
            Thread.sleep(50);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        if (monitorRef.get() == null) {
            log.debug("✅ 旧类加载器已成功卸载");
        } else {
            log.warn("⚠️ 旧类加载器仍在引用中，类可能未完全卸载");
        }
    }

}